package org.mashart.pink.m;

import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;

import org.mashart.pink.m.gps.InternalGpsConnector;
import org.mashart.pink.m.gps.BtConnector;
import org.mashart.pink.m.gps.DeviceConnector;
import org.mashart.pink.m.gps.Receiver;
import org.mashart.pink.m.store.FileManager;
import org.mashart.pink.m.store.RecordManager;
import org.mashart.pink.m.store.Preferences;
import org.mashart.pink.m.ws.FakeWsConnector;
import org.mashart.pink.m.ws.RestfulClient;
import org.mashart.pink.m.ws.WsConnector;
import org.mashart.pink.m.gui.Gui;
import org.mashart.pink.m.gui.PreferencesGUI;

/**
 * App main class that implements standard MIDlet methods, like
 * <code>startApp()</code>, <code>destroyApp()</code> and
 * <code>pauseApp()</code>
 */
public class PinkrMIDlet extends MIDlet {

    /**
     * Main class instance for user interface.
     * Initialized in <code>startApp()</code>
     */
	private Gui gui;

    private InternalGpsConnector internalConnector;
    private BtConnector btConnector;

    /**
     * GPS connector in use
     */
    private DeviceConnector conn;

    /**
     * Record store manager
     */
    private RecordManager recordManager;

    /**
     * File manager
     */
    private FileManager fileManager;

    /**
     * HTTP RESTful client
     */
    private WsConnector ws_client;

    /**
     * Instance of a class that collects data from a GPS device.
     * Running in a separate thread.
     * Initialized in <code>startApp()</code>
     */
    private Receiver recv;

    /**
     * true when application is paused
     * false otherwise
     */
	private boolean isPaused;

    /**
     * true when a round is started
     */
    private boolean roundStarted;

    /**
     * Constant value representing device type
     */
    public static final int INTERNAL_DEVICE_TYPE = 0;
    public static final int BT_DEVICE_TYPE = 1;
    private static final String tBuiltin = "in";
    private static final String tBluetooth = "bt";


    /**
     * Constructor. Doesn't take any arguments
     */
	public PinkrMIDlet() {
		isPaused = false;
        roundStarted = false;
	}

    /**
     * Called on the application execution termination.
     * Should close all open connections (Bluetooth & HTTP to the server)
     */
	public void destroyApp(boolean unconditional)
            throws MIDletStateChangeException {
	}

    /**
     * called when app has to be suspended
     * user's intention or mobile device
     */
	public void pauseApp() {
		isPaused = true;
        /* TODO how do we keep running in background? Reply: Done by hiding window*/
	}

    /**
     * Main method, i.e. starting point for the application.
     * Initializes UI, GPS data receiver and HTTP RESTful client
     *
     * Also, shows splash screen during initialization
     */
	public void startApp() throws MIDletStateChangeException {
		if(isPaused){
	      isPaused = false;
          /* TODO why return? */
	      return;
	    }
        /* init the GUI and show splash screen as the first thing */
		gui = new Gui();
        gui.setMidlet(this);
		gui.showSplash();

//        sendRemainingData();
//        discoverDeviceTypes();
        setPreferences(false);
	}

    /**
     * Set the preferences of the application.
     * @param forced if false, do not edit the preference if already set, if true, edit the preference regardless whether already set.
     */
    public void setPreferences(boolean forced){
        // stop the receiver first
        if(recv != null){
            recv.terminate();
            recv = null;
        }
        PreferencesGUI prefGui = new PreferencesGUI();
        prefGui.setMidlet(this);
        prefGui.showPreferencesEditor(forced);
    }



    /**
     * Send the remaining GPS data from last execution, if any.
     */
    public void sendRemainingData(){
        recordManager = RecordManager.getInstance();
        // check if there is an old record not sent yet
        if( recordManager.hasRecord() ){
            gui.showMessage("Old records found", "Old records are found. Sending old records...");
            // open record store
            recordManager.open();
            // initialize the RestfulClient
//            ws_client = new RestfulClient();
            ws_client = new FakeWsConnector();
            ws_client.configure();
            if ( ws_client.initialize() < 1 ){
                gui.showErrorDialog("Counldn't initialize the system", false);
                return;
            }
            // send the records
            recordManager.sendAll(ws_client);
            // close network connection
            ws_client.terminate();
            // close the record store
            recordManager.close();
            // delete the record
            recordManager.delete();
            gui.showMessage("Old records sent", "Old records are sent over the network, and deleted from the mobile device.");
        }
    }

    /**
     * Initialize
     */
    public void initialize(){

        /* instantiate GPS data receiver class */
        recv = new Receiver();

        recv.setDeviceConnector(conn);
        gui.setReceiver(recv);

        /* Initialize GPS data receiver instance.
         * That includes search for Bluetooth devices too */
        recv.configure();

        if (recv.initialize() < 1) {
            /* TODO more helpful error messages */
            gui.showErrorDialog("Counldn't initialize the system", false);
        }
        /**
         * TODO we have a debug screen only for the moment
         *      extend this into a fancy UI */
        gui.showReadyConsole();
    }

    /**
     * Discover built-in GPS
     */
    public void discoverDeviceTypes(){
        gui.showMessage("Scanning...","Scanning for GPS device, please wait.");
        boolean internalGpsFound;
        boolean btLocalDeviceFound;
        // discover built-in GPS
        internalConnector = new InternalGpsConnector();
        internalConnector.configure();
        internalGpsFound = internalConnector.initialize() > 0;

        // discover bluetooth GPS
        btConnector = new BtConnector();
        btConnector.configure();
        btLocalDeviceFound = btConnector.discover() > 0;

        gui.showDeviceTypesConsole(internalGpsFound, btLocalDeviceFound);
    }

    /**
     *
     */
    public void setDeviceType(int type){
        if(type == INTERNAL_DEVICE_TYPE){
            conn = internalConnector;
            initialize();
        }else if(type == BT_DEVICE_TYPE){
            conn = btConnector;
            gui.showMessage("Searching...", "Searching for bluetooth remote device.\nPlease wait...");
            btConnector.discoverRemoteDevices();
            gui.showBtDevicesConsole(btConnector.getRemoteDeviceNames());
        }
    }

    /**
     *
     */
    public void setDevice(String type, String url){
        if(type.equals(tBuiltin)){
            internalConnector = new InternalGpsConnector();
            internalConnector.configure();
            conn = internalConnector;
        }else if(type.equals(tBluetooth)){
            btConnector = new BtConnector();
            btConnector.configure();
            btConnector.setBtConnectoinURL(url);
            conn = btConnector;
        }
        initialize();
    }

    /**
     * Set a bluetooth GPS device connector
     */
    public void setBtRemoteDevice(int remoteDeviceIndex){
        ( (BtConnector) conn ).setRemoteDevice(remoteDeviceIndex);
        initialize();
    }

    /**
     * Get bluetooth Connection URL
     */
    public String getBtConnectionURL(){
        return ( (BtConnector) conn ).getBtConnectoinURL();
    }

    /**
     * Set bluetooth Connection URL
     */
    public void setBtConnectionURL(String url){
        ( (BtConnector) conn ).setBtConnectoinURL(url);
    }

    /**
     * Starts the GPS device
     */
    public void startRound(){
        // if already running
        if(roundStarted){
            return;
        }
        recv.startRound();
        roundStarted = true;
        gui.showStartedConsole();
    }

    /**
     * Pause a round
     */
    public void pauseRound(){
        recv.pauseRound();
    }

    /**
     * Resume a round
     */
    public void resumeRound(){
        recv.resumeRound();
    }

    /**
     * Stops the GPS device
     */
    public void stopRound(){
        // if already stopped
        if(!roundStarted){
            return;
        }
        recv.stopRound();
        roundStarted = false;
        gui.showStoppedConsole();
    }

    /**
     * Terminate
     */
    public void terminate(){
        if(roundStarted){
            stopRound();
        }
        if(recv != null){
            recv.terminate();
            recv = null;
        }
    }
}
